package org.example.gateway.config;

import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import org.example.gateway.config.properties.AccessManagerProperties;
import org.example.gateway.config.properties.AccessProperties;
import org.example.gateway.config.properties.AccessRoutesProperties;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;

import java.net.URI;
import java.util.Set;

/**
 * 鉴权管理器自定义
 */
@Component
@AllArgsConstructor
public class JwtAccessManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    private  AccessRoutesProperties accessRoutesProperties;
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext object) {
        URI uri= object.getExchange().getRequest().getURI();
        String uriPath=uri.getPath();
        AccessManagerProperties accessManagerProperties=new AccessManagerProperties(accessRoutesProperties);
        //资源鉴权
        Mono<Boolean> resourceManager=authentication
                .filter(Authentication::isAuthenticated)
                .cast(OAuth2Authentication.class)
                .map(OAuth2Authentication::getOAuth2Request)
                .map((map)->{
                    if(ObjectUtil.isNotNull(map.getResourceIds())&&map.getResourceIds().size()>0){
                        return map.getResourceIds().stream().anyMatch(re->
                                this.matchResourceList(accessManagerProperties,uriPath,re));
                    }
                    return true;
                })
                .defaultIfEmpty(false);
        //通过角色、权限鉴权
        Mono<Boolean> accessManager= authentication
                .filter(Authentication::isAuthenticated)
                .cast(OAuth2Authentication.class)
                .map(OAuth2Authentication::getOAuth2Request)
                .map((map)->{
                    if(ObjectUtil.isNotNull(map.getResourceIds())&&map.getResourceIds().size()>0){
                        return map.getResourceIds().stream().anyMatch(re->
                                this.matchAuthUrlScope(accessManagerProperties,map.getScope(),re));
                    }
                    return true;
                })
                .defaultIfEmpty(false);
    return  Mono.zip(resourceManager,accessManager)
            //通过资源鉴权
            .filter(Tuple2::getT1)
            //通过角色、权限鉴权
            .map(x->new AuthorizationDecision(x.getT2()))
            .defaultIfEmpty(new AuthorizationDecision(false));
    }






    public boolean  matchAuthUrlScope(AccessManagerProperties accessManagerProperties,Set<String> scope, String resource){
        AccessProperties accessProperties= accessManagerProperties.getResource().get(resource);
        if(ObjectUtil.isNotEmpty(accessProperties)){
            if(accessProperties.getEnabled()){
                return accessProperties.isScope(scope);
            }
            return true;
        }
        return false;
    }

    /**
     * 验证是否是资源地址
     * @param url
     * @return
     */
    public boolean matchResourceList(AccessManagerProperties accessManagerProperties,String url,String resource)
    {
        AccessProperties accessProperties= accessManagerProperties.getResource().get(resource);
        if(ObjectUtil.isNotEmpty(accessProperties)){
            if(accessProperties.getEnabled()){
                return  accessProperties.isAuthUrls(url);
            }
            return true;
        }
        return false;
    }

}
